
package com.huaweicloud.saaslanding.install.json;

import com.alibaba.fastjson.JSONObject;
import com.huaweicloud.saaslanding.basic.constant.EnvConstant;
import com.huaweicloud.saaslanding.basic.enumeration.KeyPathEnum;
import com.huaweicloud.saaslanding.basic.enumeration.SecretNameEnum;
import com.huaweicloud.saaslanding.basic.enumeration.deployment.KeyEnum;
import com.huaweicloud.saaslanding.basic.enumeration.deployment.SslStoreEnum;
import com.huaweicloud.saaslanding.basic.routelink.constant.RouteLinkConstant;
import com.huaweicloud.saaslanding.huawei.cce.enumeration.ImagePullPolicyEnum;
import com.huaweicloud.saaslanding.huawei.cce.enumeration.ServiceSpecTypeEnum;
import com.huaweicloud.saaslanding.huawei.cce.model.Container;
import com.huaweicloud.saaslanding.huawei.cce.model.ContainerEnv;
import com.huaweicloud.saaslanding.huawei.cce.model.DeploymentSelector;
import com.huaweicloud.saaslanding.huawei.cce.model.DeploymentSpec;
import com.huaweicloud.saaslanding.huawei.cce.model.DeploymentTemplate;
import com.huaweicloud.saaslanding.huawei.cce.model.DeploymentTemplateSpec;
import com.huaweicloud.saaslanding.huawei.cce.model.ImagePullSecret;
import com.huaweicloud.saaslanding.huawei.cce.model.Label;
import com.huaweicloud.saaslanding.huawei.cce.model.Metadata;
import com.huaweicloud.saaslanding.huawei.cce.model.ServiceSpec;
import com.huaweicloud.saaslanding.huawei.cce.model.ServiceSpecPort;
import com.huaweicloud.saaslanding.huawei.cce.model.Volume;
import com.huaweicloud.saaslanding.huawei.cce.model.VolumeMount;
import com.huaweicloud.saaslanding.huawei.cce.model.VolumeSecret;
import com.huaweicloud.saaslanding.huawei.cce.request.CceDeploymentCreateRequest;
import com.huaweicloud.saaslanding.huawei.cce.request.CceServiceCreateRequest;
import com.huaweicloud.saaslanding.huawei.cce.util.CceDeploymentUtil;
import com.huaweicloud.saaslanding.huawei.elb.enumeration.ProtocolEnum;
import com.huaweicloud.saaslanding.install.properties.Skywalking;
import com.huaweicloud.saaslanding.install.properties.Ssl;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * CCE 相关请求体构建
 *
 * @since 2022-07-29
 */
@Component
@Slf4j
public class CceRequestBody {

    private static final int VOLUME_SECRET_DEFAULT_MODE = 420;

    private static Ssl ssl;

    private static Skywalking skywalking;

    @Autowired
    public void setSsl(Ssl ssl) {
        CceRequestBody.ssl = ssl;
    }

    @Autowired
    public void setSkywalking(Skywalking skywalking) {
        CceRequestBody.skywalking = skywalking;
    }

    /**
     * 创建 CCE deployment 请求体
     *
     * @param valuesMap 键值对
     * @return 请求体
     */
    public static CceDeploymentCreateRequest createDeploymentBody(Map<String, String> valuesMap, boolean isHttps) {
        CceDeploymentCreateRequest deploymentCreateRequest = new CceDeploymentCreateRequest();

        String serviceName = valuesMap.get("serviceName");
        String image = valuesMap.get("swrTag");

        Metadata metadata = new Metadata();
        metadata.setName(serviceName);
        Label label = new Label();
        label.setApp(serviceName).setVersion("v1").setComponent(serviceName);
        metadata.setLabels(label);
        deploymentCreateRequest.setMetadata(metadata);

        DeploymentSpec spec = new DeploymentSpec();
        spec.setReplicas(1);

        DeploymentSelector selector = new DeploymentSelector();
        selector.setMatchLabels(label);
        spec.setSelector(selector);

        DeploymentTemplate template = new DeploymentTemplate();
        Metadata templateMetadata = new Metadata();
        templateMetadata.setLabels(label);
        template.setMetadata(templateMetadata);

        DeploymentTemplateSpec templateSpec = new DeploymentTemplateSpec();
        templateSpec.setVolumes(getDeploymentVolumes(isHttps));

        List<Container> containers = new ArrayList<>();
        Container container = new Container();
        container.setImage(image);
        container.setEnv(getEnv(valuesMap));
        container.setVolumeMounts(getDeploymentVolumeMounts(isHttps));
        container.setImagePullPolicy(ImagePullPolicyEnum.IF_NOT_PRESENT.getPolicy()).setName(serviceName);
        container.setResources(CceDeploymentUtil.getContainerResource());

        containers.add(container);
        templateSpec.setContainers(containers);

        List<ImagePullSecret> imagePullSecrets = new ArrayList<>();
        imagePullSecrets.add(new ImagePullSecret("default-secret"));
        templateSpec.setImagePullSecrets(imagePullSecrets);

        template.setSpec(templateSpec);
        spec.setTemplate(template);
        deploymentCreateRequest.setSpec(spec);

        return deploymentCreateRequest;
    }

    private static List<ContainerEnv> getEnv(Map<String, String> valuesMap) {
        List<ContainerEnv> envs = new ArrayList<>();
        envs.add(new ContainerEnv(EnvConstant.NACOS_ENABLED, valuesMap.get("nacosEnabled")));
        envs.add(new ContainerEnv(EnvConstant.NACOS_NAMESPACE, valuesMap.get("nacosNamespace")));
        envs.add(new ContainerEnv(EnvConstant.NACOS_GROUP, valuesMap.get("nacosGroup")));
        envs.add(new ContainerEnv(EnvConstant.CSE_ENABLED, valuesMap.get("cseEnabled")));
        envs.add(new ContainerEnv(EnvConstant.PAAS_MONITORING_GROUP, valuesMap.get("appName")));
        envs.add(new ContainerEnv(EnvConstant.PAAS_APP_NAME, valuesMap.get("serviceName")));
        envs.add(new ContainerEnv(EnvConstant.PAAS_NAMESPACE, valuesMap.get("appName")));
        envs.add(new ContainerEnv(EnvConstant.PAAS_PROJECT_ID, valuesMap.get("projectId")));
        envs.add(new ContainerEnv(EnvConstant.PROFILE, valuesMap.get("profile")));
        envs.add(new ContainerEnv(EnvConstant.SPRING_APPLICATION_NAME, valuesMap.get("serviceName")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_DISCOVERY_ADDRESS, valuesMap.get("discoveryAddress")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_DISCOVERY_APPNAME, valuesMap.get("appName")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_DISCOVERY_VERSION, "0.0.1"));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_SERVERADDR, valuesMap.get("configAddress")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_ENABLED, "true"));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_ACCESSKEY, valuesMap.get("accessKey")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_SECRETKEY, valuesMap.get("secretKey")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_ACCOUNT_NAME, valuesMap.get("cseName")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_ACCOUNT_PASSWORD,
            valuesMap.get("csePassword")));
        envs.add(new ContainerEnv(EnvConstant.SERVICECOMB_CONFIG_CREDENTIALS_PROJECT, valuesMap.get("cseProject")));
        envs.add(new ContainerEnv(EnvConstant.ROUTE_LINK, RouteLinkConstant.ROUTE_LINK_KEY_BASE));
        // SkyWalking
        if (skywalking.isEnabled()) {
            JSONObject instancePropertiesJson = new JSONObject();
            instancePropertiesJson.put(RouteLinkConstant.TAG_INSTANCE_VERSION, "0.0.1");
            instancePropertiesJson.put(RouteLinkConstant.TAG_INSTANCE_ROUTE_LINKS,
                RouteLinkConstant.ROUTE_LINK_KEY_BASE);

            envs.add(new ContainerEnv(EnvConstant.SKYWALKING_ENABLE, "true"));
            envs.add(new ContainerEnv(EnvConstant.SW_AGENT_COLLECTOR_BACKEND_SERVICES, skywalking.getServer()));
            envs.add(new ContainerEnv(EnvConstant.SW_AGENT_NAME, valuesMap.get("serviceName")));
            envs.add(new ContainerEnv(EnvConstant.SW_INSTANCE_PROPERTIES_JSON, instancePropertiesJson.toString()));
        } else {
            envs.add(new ContainerEnv(EnvConstant.SKYWALKING_ENABLE, "false"));
        }

        // springdoc
        envs.add(new ContainerEnv(EnvConstant.SWAGGER_ENABLED,valuesMap.get("swaggerEnabled")));
        return envs;
    }

    private static List<VolumeMount> getDeploymentVolumeMounts(boolean isHttps) {
        List<VolumeMount> volumeMounts = new ArrayList<>();
        if (isHttps) {
            VolumeMount trustStore = new VolumeMount();
            trustStore.setName(SslStoreEnum.TRUST_STORE.getName())
                .setReadOnly(true)
                .setMountPath(ssl.getTrustStorePath())
                .setSubPath(SslStoreEnum.TRUST_STORE.getSubPath());
            volumeMounts.add(trustStore);

            VolumeMount keyStore = new VolumeMount();
            keyStore.setName(SslStoreEnum.KEY_STORE.getName())
                .setReadOnly(true)
                .setMountPath(ssl.getKeyStorePath())
                .setSubPath(SslStoreEnum.KEY_STORE.getSubPath());
            volumeMounts.add(keyStore);
        }

        VolumeMount rootKey = new VolumeMount();
        rootKey.setName(KeyEnum.ROOT_KEY.getName())
            .setReadOnly(true)
            .setMountPath(KeyPathEnum.ROOT_KEY.getPath())
            .setSubPath(KeyEnum.ROOT_KEY.getSubPath());
        volumeMounts.add(rootKey);

        VolumeMount workPw = new VolumeMount();
        workPw.setName(KeyEnum.WORK_PW.getName())
            .setReadOnly(true)
            .setMountPath(KeyPathEnum.WORK_PW.getPath())
            .setSubPath(KeyEnum.WORK_PW.getSubPath());
        volumeMounts.add(workPw);

        return volumeMounts;
    }

    private static List<Volume> getDeploymentVolumes(boolean isHttps) {
        List<Volume> volumes = new ArrayList<>();
        if (isHttps) {
            Volume trustStore = new Volume();
            trustStore.setName(SslStoreEnum.TRUST_STORE.getName())
                .setSecret(new VolumeSecret(SecretNameEnum.CLOUD_SERVICE.getName(), VOLUME_SECRET_DEFAULT_MODE));
            volumes.add(trustStore);

            Volume keyStore = new Volume();
            keyStore.setName(SslStoreEnum.KEY_STORE.getName())
                .setSecret(new VolumeSecret(SecretNameEnum.CLOUD_SERVICE.getName(), VOLUME_SECRET_DEFAULT_MODE));
            volumes.add(keyStore);
        }

        VolumeSecret keySecret = new VolumeSecret(SecretNameEnum.KEY.getName(), VOLUME_SECRET_DEFAULT_MODE);
        Volume rootKey = new Volume();
        rootKey.setName(KeyEnum.ROOT_KEY.getName()).setSecret(keySecret);
        volumes.add(rootKey);

        Volume workPw = new Volume();
        workPw.setName(KeyEnum.WORK_PW.getName()).setSecret(keySecret);
        volumes.add(workPw);

        return volumes;
    }

    /**
     * 创建 ClusterIP Service 请求体
     *
     * @param name 名称
     * @param serviceName 服务名称
     * @param port 端口
     * @return 请求体
     */
    public static CceServiceCreateRequest createClusterIPServiceBody(String name, String serviceName, Integer port,
        Integer targetPort) {
        CceServiceCreateRequest serviceCreateRequest = new CceServiceCreateRequest();

        Metadata metadata = new Metadata();
        metadata.setName(name);
        Label label = new Label();
        label.setApp(serviceName).setVersion("v1");
        metadata.setLabels(label);
        serviceCreateRequest.setMetadata(metadata);

        ServiceSpec spec = getServiceSpec(serviceName, port, targetPort);
        serviceCreateRequest.setSpec(spec);

        return serviceCreateRequest;
    }

    private static ServiceSpec getServiceSpec(String serviceName, Integer port, Integer targetPort) {
        ServiceSpec spec = new ServiceSpec();
        spec.setType(ServiceSpecTypeEnum.CLUSTER_IP.getType());
        Label selector = new Label();
        selector.setApp(serviceName).setVersion("v1");
        spec.setSelector(selector);
        List<ServiceSpecPort> ports = new ArrayList<>();
        ServiceSpecPort specPort = new ServiceSpecPort();
        specPort.setName("cce-service-0")
            .setProtocol(ProtocolEnum.TCP.getType())
            .setPort(port)
            .setTargetPort(targetPort);
        ports.add(specPort);
        spec.setPorts(ports);
        return spec;
    }

}
